home *** CD-ROM | disk | FTP | other *** search
- /*
- * CPDIST.C
- */
-
- /*
- * (c)Copyright 1992-93 by Tobias Ferber.
- *
- * This file is part of CPDIST.
- *
- * CPDIST is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 1 of the License,
- * or (at your option) any later version.
- *
- * CPDIST is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with CPDIST; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- #include <ctype.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include "cpdist.h"
- #include "filecopy.h"
-
- /* If you don't want CPDIST to interact with the user's keyboard via
- * getkey() then you should compile this file with the -DNO_GETKEY option.
- * copy_files() (the only function that uses getkey() will then prompt
- * waiting for a string instead of a character
- */
-
- #ifndef NO_GETKEY
- #include "getkey.h"
- #endif
-
- static char rcs_id[]= "$VER: $Id$";
-
- /* scanner modes */
-
- typedef enum { outer_mode, /* reading word seperating chars */
- word_mode, /* collecting chars for a word */
- string_mode, /* collecting chars for a string */
- remark_mode, /* waiting for the end of line */
- comment_mode, /* waiting for the matching '*' and '/' */
- return_mode, /* word or string complete */
- error_mode /* an error occured! return NULL */
- } smode_t;
-
-
- char *getword(FILE *fp)
- /* This function will skip everything up to the beginning of the next word
- * that is not inside of a comment and then copy the following word into a
- * static buffer and terminate it with a '\0' character. A pointer to this
- * buffer will be returned.
- * A comment is (as in C) everything that stands between a '/' + '*' token
- * and the matching '*' + '/'. C++ like comments (named remarks here) are
- * also suported. (They begin with two slashes '/' + '/' and end with the
- * end of the line they appear in.)
- * Note: This file will use lerror() to report error messages to the user.
- * If anyone else but this function will read from the given file 'fp' then
- * the line numbers reported by lerror() may become wrong! */
- {
- static char buf[128]; /* should be enough for _one_ word */
- static int line= 1; /* line number */
- int n=0; /* #of chars in buf[] */
- unsigned char c; /* currently read character */
-
- smode_t smode= outer_mode; /* scanner mode */
-
- while(smode!=return_mode && smode!=error_mode && !feof(fp))
- {
- c= fgetc(fp);
-
- if(feof(fp) && c!=(unsigned char)EOF)
- { echo("In module " __FILE__ ": feof()==TRUE but fgetc()==0x%02x",
- (int)(c&0xFF));
- c= (unsigned char)EOF;
- }
-
- switch(c)
- {
- case ' ': case '\t': case ':':
- switch(smode)
- {
- case word_mode:
- if(c==':') buf[n++]=':';
- buf[n++]= '\0';
- smode= return_mode;
- break;
- case string_mode:
- buf[n++]= c;
- break;
- }
- break;
-
- case '\n': case '\r':
- switch(smode)
- {
- case word_mode:
- buf[n++]= '\0';
- smode= return_mode;
- break;
- case string_mode:
- lerror(line,"unterminated string; missing quotes `\"'");
- smode= error_mode;
- break;
- case remark_mode:
- smode= outer_mode;
- break;
- }
- line++;
- break;
-
- case '\"':
- switch(smode)
- {
- case outer_mode:
- smode= string_mode;
- break;
- case string_mode:
- buf[n++]= '\0';
- smode= return_mode;
- break;
- case word_mode:
- buf[n++]= '\0';
- ungetc(c,fp);
- smode= return_mode;
- break;
- }
- break;
-
- case '/':
- switch(smode)
- {
- case outer_mode:
- switch(c= fgetc(fp))
- {
- case '*':
- smode= comment_mode;
- break;
- case '/':
- smode= remark_mode;
- break;
- default:
- ungetc(c,fp);
- break;
- }
- break;
- case word_mode:
- case string_mode:
- buf[n++]= c;
- break;
- }
- break;
-
- case '*':
- switch(smode)
- {
- case comment_mode:
- switch(c= fgetc(fp))
- {
- case '/':
- smode= outer_mode;
- break;
- default:
- ungetc(c,fp);
- break;
- }
- break;
- case word_mode:
- case string_mode:
- buf[n++]= c;
- break;
- }
- break;
-
- case (unsigned char)EOF:
- switch(smode)
- {
- case word_mode:
- buf[n++]= '\0';
- smode= return_mode;
- break;
- case string_mode:
- lerror(line,"unterminated string at EOF; closing quotes `\"' missing");
- smode= error_mode;
- break;
- case comment_mode:
- lerror(line,"unterminated comment at EOF; closing `*/' missing");
- smode= error_mode;
- break;
- }
- break;
-
- default:
- switch(smode)
- {
- case outer_mode:
- smode= word_mode;
- /* fall through */
- case word_mode:
- case string_mode:
- buf[n++]= c;
- break;
- }
- break;
- }
- }
-
- buf[n]= '\0'; /* better add one more... */
-
- return (smode==error_mode) ? (char *)0L : buf;
- }
-
- typedef struct fnode {
- struct fnode *next;
- char *fname;
- } fnode;
-
-
- /* collect states */
-
- typedef enum { okay,
- no_such_file,
- out_of_memory,
- syntax_error,
- } state_t;
-
- fnode *collect_files(FILE *fp)
- /* Here we collect the filenames in the given distfile pointer 'fp' and
- * add them to a list of fnode structures. This function reports errors
- * (if there are any) via the echo() function (see main.c or so) */
- {
- char *fname;
-
- fnode *flist = (fnode *)0L,
- *last = (fnode *)0L;
-
- int do_collect= 1; /* collect files (1) or skip until next label (0) */
- int files= 0; /* #of collected files */
- int errors= 0; /* #of syntax errors in distfile */
- int black_sheeps= 0; /* #of inaccessable items (e.g. dirs) */
-
- state_t state= okay;
-
- do
- { fname= getword(fp);
-
- if(fname && *fname)
- {
- if(fname[strlen(fname)-1]==':')
- do_collect= ln_member(fname);
-
- else if(do_collect)
- {
- if(CP_CHECKEXISTS)
- {
- FILE *tf;
- if( tf= fopen(fname,"r") )
- fclose(tf);
- else
- { perror(fname);
- black_sheeps++;
- state= no_such_file;
- }
- }
-
- if(state != no_such_file)
- {
- fnode *this;
-
- if( this= (fnode *)malloc(sizeof(fnode)) )
- {
- if( this->fname= (char *)malloc((strlen(fname)+1)*sizeof(char)) )
- {
- strcpy(this->fname, fname);
- this->next= (fnode *)0L;
-
- if(last) last->next= this;
- else flist= this;
- last= this;
- files++;
- }
- else
- { free(this);
- /* this= (fnode *)0L; */
- state= out_of_memory;
- }
- }
- else state= out_of_memory;
- }
- }
- }
- else if(!feof(fp))
- { errors++;
- if(!CP_IGNOREERRORS)
- state= syntax_error;
- }
-
- if((state==no_such_file) && CP_KEEPGOING)
- state= okay;
-
- } while((state==okay) && !feof(fp));
-
- if(state!=okay)
- {
- /* purge the file list */
- while(flist)
- { last= flist;
- flist= flist->next;
- if(last->fname)
- free(last->fname);
- free(last);
- }
-
- if(state==out_of_memory)
- echo("Ran out of memory after having collected %d %s",
- files, (files==1) ? "filename" : "filenames");
- echo("going down.");
- }
-
- if(errors && CP_IGNOREERRORS)
- echo("%d %s -- You should revise your Distfile...",
- errors, (errors==1) ? "Error" : "Errors");
-
- if(black_sheeps && CP_KEEPGOING)
- echo("Couldn't access %d %s",
- black_sheeps, (black_sheeps==1) ? "item" : "items");
-
- return flist;
- }
-
- #ifdef STRIP_PATHNAMES
- char *strfn(char *s)
- /* returns the filename w/o leading pathname */
- { char *t;
- for(t=s; *s!='\0'; s++)
- if(*s==':' || *s=='/' || *s=='\\')
- t= &s[1];
- return t;
- }
- #else
- #define strfn(s) (s)
- #endif /* STRIP_PATHNAMES */
-
-
- int copy_files(fnode *flist, char *pname)
- /* Here we copy all files in 'flist' to the given path 'pname'.
- * The destination filename will be generated by concatenation of
- * 'pname' and the actual filename in a node of 'flist'. So 'pname'
- * must have a trailing slash '/', colon ':' or backslash '\' or
- * whatever.
- * While copying the files, all nodes in flist will be free()d. */
- {
- char outname[MAXIMUM_PATHNAME_LENGTH];
- char *catpoint;
- int numfiles= 0;
- long numbytes= 0;
- int err= 0;
-
- strcpy(outname, pname);
- catpoint= &outname[strlen(outname)];
-
- if(!CP_DRYRUN)
- (void)fc_setbuf(global_buffersize);
-
- while(flist && !err)
- {
- int dothis= 1; /* 1=copy/replace, 0=skip, -1=??? */
-
- fnode *this= flist;
- flist= flist->next;
-
- if(this->fname)
- {
- strcpy(catpoint, strfn(this->fname));
-
- if(!CP_REPLACEALL && !CP_DRYRUN)
- {
- FILE *fp;
- if(fp= fopen(outname,"r"))
- {
- int key;
- fclose(fp);
-
- fprintf(stderr,"%s already exists. replace it? (y/n/a/q) ",outname);
- fflush(stderr);
-
- do {
-
- #ifdef NO_GETKEY
- char answerbuf[10];
- fgets(answerbuf, sizeof(answerbuf), stdin);
- key= (int)answerbuf[0];
-
- #else /* !NO_GETKEY */
- key= (int)getkey();
-
- #endif /* NO_GETKEY */
-
- switch( key )
- {
- case 'y': case 'Y':
- #ifndef NO_GETKEY
- puts("yes");
- #endif
- dothis= 1;
- break;
- case 'n': case 'N':
- #ifndef NO_GETKEY
- puts("no");
- #endif
- dothis= 0;
- break;
- case 'a': case 'A':
- #ifndef NO_GETKEY
- puts("all");
- #endif
- global_opts |= OPT_REPLACEALL;
- dothis= 1;
- break;
- case 'q': case 'Q':
- #ifndef NO_GETKEY
- puts("quit");
- #endif
- dothis= 0;
- err= 1; /* positive error codes are not ignored */
- break;
- default:
- dothis= -1; /* hack: means 'invalid choice' */
- break;
- }
-
- } while( dothis < 0 );
- }
- }
-
- if(dothis)
- {
- if(!CP_SILENT)
- {
- printf( (CP_DRYRUN ? (strchr(outname,' ') ? "\"%s\" " : "%s ")
- : " %s"), outname );
- fflush(stdout);
- }
-
- if(!CP_DRYRUN)
- {
- long n;
- FILE *src, *dst;
-
- if(src= fopen(this->fname,"rb"))
- {
- if(dst= fopen(outname,"wb"))
- {
- if( (n= filecopy(src,dst,0L)) < 0L )
- {
- if(!CP_SILENT)
- putchar('\n');
- perror( (n==(-1L)) ? this->fname : outname );
- echo("Error copying `%s'.",this->fname);
- err= n;
- }
- else
- { numbytes+= n;
- if(!CP_SILENT)
- { printf(" (%ld)",n);
- fflush(stdout);
- }
- }
- fclose(dst);
- }
- else
- { if(!CP_SILENT)
- putchar('\n');
- echo("Can't write to `%s'.",outname);
- err= -2L;
- }
- fclose(src);
- }
- else
- { if(!CP_SILENT)
- putchar('\n');
- echo("Can't access input file `%s'.",this->fname);
- err= -1L;
- }
- }
-
- if(!err)
- {
- ++numfiles;
-
- if(!CP_SILENT && !CP_DRYRUN)
- putchar('\n');
- }
-
- if(CP_SILENT && !CP_DRYRUN)
- { printf("%d %s copied\r",numfiles, (numfiles==1) ? "file" : "files");
- fflush(stdout);
- }
- }
- free(this->fname);
- }
- free(this);
-
- /* Negative error codes indicate I/O errors which are ignored
- * if w/ the '--keep-going' option given in the command-line.
- * Positive values indicate fatal errors and can be used to
- * force exiting from this function */
-
- if(err<0 && CP_KEEPGOING)
- err= 0L;
- }
-
- if(!CP_DRYRUN)
- (void)fc_setbuf(0L);
-
- if(!CP_DRYRUN || (CP_DRYRUN && CP_SILENT))
- printf("%d %s%s",numfiles, (numfiles==1) ? "file" : "files",
- (CP_DRYRUN) ? ".\n" : " copied.");
-
- if(!CP_DRYRUN)
- printf(" (%ld %s)\n", numbytes, (numbytes==1) ? "byte" : "bytes");
-
- return err;
- }
-
-
- int prepare_distribution(char *fname, char *pname)
- {
- FILE *fp;
- int err= 0;
-
- if( fp= fopen(fname,"r") )
- {
- fnode *flist;
- if( flist= collect_files(fp) )
- {
- err= copy_files(flist, pname);
- }
- fclose(fp);
- }
- else
- { perror(fname);
- echo("Can't access `%s'\n",fname);
- err= 1;
- }
- return err;
- }
-